# coding: utf8
"""
通用字符串处理工具
"""

import datetime
import hashlib
import re
import secrets
import uuid
from ast import literal_eval
from decimal import Decimal

_PROTECTED_TYPES = (
    type(None), int, float, Decimal, datetime.datetime, datetime.date,
    datetime.time,
)


class String(object):
    """字符串处理类"""

    @staticmethod
    def conceal(_str) -> str:
        """
        遮掩字符串中间部分为*号
        :param _str: 要处理的字符串
        :return: 最长三位的字符串, 如果***表示失败
        """
        try:
            value_str = str(_str)
            if len(value_str) > 2:
                return f"{value_str[0]}*{value_str[-1]}"
            elif len(value_str) > 0:
                return f"*{value_str[-1]}"
            else:
                return value_str
        except Exception as ex:
            return "***"

    @staticmethod
    def md5(_str: str, upper=False):
        """
        对字符串md5
        :param _str: 要处理的字符串
        :param upper: 是否全转为大写
        :return: md5处理后字符串
        """
        md5str = hashlib.md5(_str.encode()).hexdigest()
        if upper:
            return md5str.upper()
        return md5str

    @staticmethod
    def hash(_str: str, upper=False):
        """
        对字符串hash
        :param _str: 要处理的字符串
        :param upper: 是否全转为大写
        :return: hash处理后字符串
        """
        sha256str = hashlib.sha256(_str.encode()).hexdigest()
        if upper:
            return sha256str.upper()
        return sha256str

    @staticmethod
    def uuid1(sep=False):
        """
        生成唯一uuid1码
        :param sep: 包含分隔符
        :return: uuid码，不带分隔符长度32，带分隔符36
        """
        uuid_str = uuid.uuid1().__str__()
        if sep:
            return uuid_str
        return uuid_str.replace("-", "")

    @staticmethod
    def uuid5(sep=False, namespace=""):
        """
        生成唯一uuid5码
        :param sep: 包含分隔符
        :param namespace:   命名空间字符串，
        :return: uuid码，不带分隔符长度32，带分隔符36
        """
        uuid_str = uuid.uuid3(uuid.NAMESPACE_DNS, namespace).__str__()
        if sep:
            return uuid_str
        return uuid_str.replace("-", "")

    @staticmethod
    def force_str(s, encoding='utf-8', strings_only=False, errors='strict'):
        """强制转为字符型，取自django.utils.encoding.py
        Similar to smart_str(), except that lazy instances are resolved to
        strings, rather than kept as lazy objects.

        If strings_only is True, don't convert (some) non-string-like objects.
        """
        # Handle the common case first for performance reasons.
        if issubclass(type(s), str):
            return s
        if strings_only and isinstance(s, _PROTECTED_TYPES):
            return s
        try:
            if isinstance(s, bytes):
                s = str(s, encoding, errors)
            else:
                s = str(s)
        except UnicodeDecodeError as e:
            raise UnicodeDecodeError(s, *e.args)
        return s

    @staticmethod
    def get_random_string(length=12,
                          allowed_chars='abcdefghijklmnopqrstuvwxyz'
                                        'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'):
        """
        Return a securely generated random string.

        The default length of 12 with the a-z, A-Z, 0-9 character set returns
        a 71-bit value. log_2((26+26+10)^12) =~ 71 bits
        """
        return ''.join(secrets.choice(allowed_chars) for i in range(length))

    @staticmethod
    def get_random_secret_key(length=50):
        """
        Return a 50 character random string usable as a SECRET_KEY setting value.
        """
        chars = 'abcdefghijklmnopqrstuvwxyz' \
                'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*(-_=+)'
        return String.get_random_string(length, chars)

    @staticmethod
    def split(string: str, pattern=r"[, |;，、·~\-\\\/]", strip=True,
              empty_element=False, as_type=str) -> list:
        """分割字符串，是否清理前后空字符，是否允许空元素"""
        string = string.strip() if strip else string
        elements = re.split(pattern, string) if string else []
        if not empty_element:
            # 不允许空元素
            elements = [element for element in elements if element]
        return [as_type(element) for element in elements]

    @staticmethod
    def range(start: int, end: int, contain=False):
        """是否含结尾数值"""
        return range(start, end + 1 if contain else end)

    @staticmethod
    def str_range_list(str_range: str, contain=False):
        ranges = String.split(string=str_range, as_type=int)
        return String.range(ranges[0], ranges[1], contain)

    @staticmethod
    def bool(string: str):
        """字符串转布尔值"""
        if string is None or string in [
            "None", "NONE", "", "Null", "null", "NULL"]:
            return False
        if string == "true":
            return True
        if string == "false":
            return False
        try:
            return bool(literal_eval(string))
        except:
            return False


if __name__ == "__main__":
    print(String.hash(String.md5("123456")))
    print(String.get_random_secret_key())
